package com.gateway.pay.channel.utils;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
@Lazy(false)
@Slf4j
public class SpringApplicationContext implements BeanFactoryPostProcessor, ApplicationContextAware, DisposableBean {

    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringApplicationContext.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringApplicationContext.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        if (applicationContext == null) {
            throw new RuntimeException("applicationContext is null");
        }
        return applicationContext;
    }

    public static ListableBeanFactory getBeanFactory() {
        if (beanFactory != null) {
            return beanFactory;
        }
        return getApplicationContext();
    }

    public static ConfigurableListableBeanFactory getConfigurableBeanFactory() {
        ConfigurableListableBeanFactory factory;
        if (beanFactory != null) {
            return beanFactory;
        }
        return ((ConfigurableApplicationContext) getApplicationContext()).getBeanFactory();
    }

    public static <T> T getBean(String name) {
        try {
            return (T) getBeanFactory().getBean(name);
        } catch (Exception e) {
            return null;
        }

    }

    public static <T> T getBean(Class<T> clazz) {
        try {
            return (T) getBeanFactory().getBean(clazz);
        } catch (Exception e) {
            return null;
        }
    }

    public static <T> T getBean(String name, Class<T> clazz) {
        try {
            return (T) getBeanFactory().getBean(name, clazz);
        } catch (Exception e) {
            return null;
        }
    }

    public static <T> Map<String, T> getBeansOfType(Class<T> type) {
        try {
            return getBeanFactory().getBeansOfType(type);
        } catch (Exception e) {
            return null;
        }
    }

    public static String[] getBeanNamesForType(Class<?> type) {
        try {
            return getBeanFactory().getBeanNamesForType(type);
        } catch (Exception e) {
            return null;
        }
    }

    public static String getProperty(String key) {
        return getApplicationContext().getEnvironment().getProperty(key);
    }

    public static String getApplicationName() {
        return getProperty("spring.application.name");
    }

    public static String getContextPath() {
        return getProperty("server.servlet.context-path");
    }

    public static String[] getActiveProfiles() {
        return getApplicationContext().getEnvironment().getActiveProfiles();
    }

    public static String getActiveProfile() {
        String[] activeProfiles = getActiveProfiles();
        return activeProfiles == null ? null : activeProfiles[0];
    }

    public static void publishEvent(ApplicationEvent event) {
        getApplicationContext().publishEvent(event);
    }

    public static void publishEvent(Object event) {
        getApplicationContext().publishEvent(event);
    }

    public static void clearHolder() {
        if (log.isDebugEnabled()) {
            log.debug("clearHolder");
        }
        applicationContext = null;
    }


    public static void registerBean(String beanName, Class<?> clazz) {
        // 将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) getApplicationContext();

        // 获取Bean工厂并转换为DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();

        // 通过BeanDefinitionBuilder创建Bean定义
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);

        // 设置bean属性，引属性引用已经定义的
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        // 注册Bean
        beanFactory.registerBeanDefinition(beanName, beanDefinition);

    }


    public static <T> void registerBean(String beanName, T bean) {
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) getApplicationContext();
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();

        //注册单例Bean
        beanFactory.registerSingleton(beanName, bean);
    }

    public static void registerBean(Class<?> clazz) {
        registerBean(clazz.getSimpleName(), clazz);
    }


    public static void unregisterBean(String beanName) {
        ConfigurableListableBeanFactory factory = getConfigurableBeanFactory();
        DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) factory;
        registry.destroySingleton(beanName);
    }

    @Override
    @SneakyThrows
    public void destroy() {
        SpringApplicationContext.clearHolder();
    }
}
